home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Games / TileSlide / Source / TileControl.m < prev    next >
Text File  |  1993-07-10  |  38KB  |  1,122 lines

  1.  
  2.     // TileSlide v1.0
  3.     // By Kevin Brain (ksbrain@zeus.UWaterloo.ca)
  4.     //  with SoundGenerator class by Ali Ozer 
  5.     // Released to the public domain September, 1991
  6.  
  7. #import "TileControl.h"
  8.  
  9. #import <appkit/nextstd.h>    //imports math.h,stdio.h,libc.h
  10.                             // (libc.h imports string.h)
  11. #import    <appkit/Box.h>
  12. #import    <appkit/Button.h>
  13. #import    <appkit/Cell.h>
  14. #import <appkit/Text.h>            // NXOrderStrings
  15. #import <appkit/Matrix.h>        // cellAt:: method
  16. #import <appkit/Panel.h>        // NXRunAlertPanel
  17. #import <appkit/OpenPanel.h>    // for loading background
  18. #import    <appkit/Application.h>    // NX_BASETHRESHOLD
  19. #import    <appkit/FontManager.h>    // for changing size of button fonts
  20. #import <appkit/publicWraps.h>    // for NXConvertWinNumToGlobal
  21. #import <appkit/Listener.h>        // for NX_WORKSPACEREQUEST
  22. #import <appkit/Speaker.h>        // for setSendPort
  23. #import <defaults.h>            // for using defaults system
  24.  
  25. #import <next/machparam.h>        // for DELAY (playing ending tune)
  26.  
  27.     // The scheme for the "music" is to play 2 note chords, where the notes are
  28.     // selected from arrays of chords.  The musical key changes every NOTESPERBAR notes.
  29.     // The two arrays rowNotes and colNotes store the pitches.  (The notes of the 
  30.     // rowNotes array are generally lower in pitch).  A note from each array is
  31.     // played whenever a tile is moved.  The note from the rowNotes array is selected 
  32.     // by the row number clicked, while the notes within a given key (row) of the 
  33.     // colNotes array are played sequentially.
  34. #define NUMBEROFBARS 8
  35. #define NOTESPERBAR 4
  36. #define COMPUTEBAR ((int)((numberOfMoves%(NUMBEROFBARS*NOTESPERBAR)) /NOTESPERBAR))
  37. #define SEQUENTIALNOTES ((numberOfMoves%(NUMBEROFBARS*NOTESPERBAR)) %NOTESPERBAR)
  38. #define ROWNOTE(k,n)   (MKKeyNumToFreq (rowNotes[(k%NUMBEROFBARS)][(n%5)]))
  39. #define COLNOTE(k,n)   (MKKeyNumToFreq (colNotes[(k%NUMBEROFBARS)][(n%5)]))
  40.  
  41. @implementation TileControl
  42.  
  43. - appDidInit:sender
  44. // responds as application's delegate
  45. {
  46.     NXRect contentViewFrame;
  47.     NXSize theSize;
  48.     char titleTemp[3];
  49.     const char *tmpstr;
  50.     int i,row,col,x,y;
  51.     int tempIconPosition;
  52.     unsigned int windowNum;
  53.     id    speaker = [NXApp appSpeaker];
  54.     
  55.     static NXDefaultsVector TileSlideDefaults = {
  56.         {"Picture", ""},
  57.         {"Difficulty", "Normal"},
  58.         {"UsePictures", "YES"},
  59.         {"UseNumbers", "NO"},
  60.         {"EasyBests", "999,999,999,999"},
  61.         {"NormalBests", "999,999,999,999"},
  62.         {"HardBests", "999,999,999,999"},
  63.         {"BoardLocation","314 314"},
  64.         {NULL}};
  65.  
  66.     NXRegisterDefaults([NXApp appName], TileSlideDefaults);
  67.  
  68.     tileSlideCV = [slideWindowOut contentView];
  69.     infoPanelCV = [infoPanelOut contentView];
  70.   /* set initial difficulty */
  71.     tmpstr = NXGetDefaultValue ([NXApp appName], "Difficulty");
  72.     if (sscanf(tmpstr, "%d", &NumTilesX) != 1) NumTilesX = 4;
  73.     NumTilesY = NumTilesX;
  74.  
  75.   /* retrieve bests for four challenges in each difficulty */
  76.     tmpstr = NXGetDefaultValue ([NXApp appName], "EasyBests");
  77.     if (sscanf(tmpstr, "%d %d %d %d", &bests[0][0],&bests[0][1],&bests[0][2],&bests[0][3]) != 4)
  78.         for (i=0;i<4;i++) bests[0][i] = MAXMOVES;
  79.     tmpstr = NXGetDefaultValue ([NXApp appName], "NormalBests");
  80.     if (sscanf(tmpstr, "%d %d %d %d", &bests[1][0],&bests[1][1],&bests[1][2],&bests[1][3]) != 4)
  81.         for (i=0;i<4;i++) bests[1][i] = MAXMOVES;
  82.     tmpstr = NXGetDefaultValue ([NXApp appName], "HardBests");
  83.     if (sscanf(tmpstr, "%d %d %d %d", &bests[2][0],&bests[2][1],&bests[2][2],&bests[2][3]) != 4)
  84.         for (i=0;i<4;i++) bests[2][i] = MAXMOVES;
  85.     for (row=0;row < 3;row++)
  86.         for (col=0; col < 4; col++)
  87.             if (bests[row][col] < MAXMOVES)
  88.                 [[bestsMatrixOut cellAt:row:col] setIntValue:bests[row][col]];
  89.             else
  90.                 [[bestsMatrixOut cellAt:row:col] setStringValue:"–"];
  91.  
  92.   /* initialize info panel animation variables */
  93.     [infoPanelCV getFrame:&contentViewFrame];
  94.     stripe1Positiony = 5;
  95.     stripe2Positiony = (contentViewFrame.size.height-33);
  96.     stripe1Positionx = 5;
  97.     stripe2Positionx = (contentViewFrame.size.width-33);
  98.     stripe1Movement = 5;
  99.     stripe2Movement = -5;
  100.  
  101.     tmpstr = NXGetDefaultValue ([NXApp appName], "Picture");
  102.     if ([self initAndCheckPicture:(char *)tmpstr] == NO) {
  103.         pictureLoaded = NO;  
  104.         usePictures = NO;
  105.         useNumbers = YES;
  106.         [useNumbersSwitch setEnabled:NO];
  107.         [useNumbersSwitch setIntValue:1];
  108.         [usePicturesSwitch setEnabled:NO];
  109.         [usePicturesSwitch setIntValue:0];
  110.         tempIconPosition = NX_TITLEONLY;
  111.         }
  112.     else {
  113.     
  114.         [picture getSize:(NXSize *)&theSize];
  115.         tmpstr = NXGetDefaultValue ([NXApp appName], "UsePictures");
  116.  
  117.         if (NXOrderStrings((unsigned char *) "YES",(unsigned char *) tmpstr, YES, 4, NULL) == 0) {
  118.             usePictures = YES;
  119.             [usePicturesSwitch setIntValue:1];
  120.             }
  121.         else {    
  122.             usePictures = NO;
  123.             [usePicturesSwitch setIntValue:0];
  124.             }
  125.  
  126.         if ((theSize.height >= NUMBERMINHEIGHT) && (theSize.width >= NUMBERMINWIDTH)
  127.                 && ([usePicturesSwitch intValue] == 1))
  128.             [useNumbersSwitch setEnabled:YES];
  129.         else
  130.             [useNumbersSwitch setEnabled:NO];
  131.  
  132.         tmpstr = NXGetDefaultValue ([NXApp appName], "UseNumbers");
  133.         if ((NXOrderStrings((unsigned char *) "YES",(unsigned char *) tmpstr, YES, 4, NULL) == 0)
  134.                 && (theSize.height >= NUMBERMINHEIGHT) && (theSize.width >= NUMBERMINWIDTH)) {
  135.             useNumbers = YES;
  136.             [useNumbersSwitch setIntValue:1];
  137.             [usePicturesSwitch setEnabled:YES];
  138.             if (usePictures == YES)
  139.                 tempIconPosition = NX_ICONOVERLAPS;
  140.             else
  141.                 tempIconPosition = NX_TITLEONLY;
  142.             }
  143.         else {
  144.             useNumbers = NO;
  145.             [useNumbersSwitch setIntValue:0];
  146.             [usePicturesSwitch setEnabled:NO];
  147.             if (usePictures == YES)
  148.                 tempIconPosition = NX_ICONONLY;
  149.             else
  150.                 tempIconPosition = NX_TITLEONLY;
  151.             }
  152.         tempIconPosition = NX_ICONONLY;
  153.         }
  154.     myFontManager = [FontManager new];
  155.   /* create the box that holds the tiles */
  156.     tileBox = [Box new];
  157.     [tileBox setBorderType:NX_BEZEL];
  158.     [tileBox setTitlePosition:NX_NOTITLE];
  159.     [tileBox setOffsets:(NXCoord)0 :(NXCoord)0];
  160.   /* create a box for erasing the old button position when a button is moved */
  161.     clearBox = [Box new];
  162.     [clearBox setBorderType:NX_NOBORDER];
  163.     [clearBox setTitlePosition:NX_NOTITLE];
  164.     [clearBox setOffsets:(NXCoord)0 :(NXCoord)0];
  165.  
  166.     for (i=0; i<(MAXSIZEX*MAXSIZEY); i++) {
  167.         tile[i] = [Button new];
  168.         [tile[i] setAutodisplay:NO];
  169.         sprintf(titleTemp,"%d",i+1);
  170.         [tile[i] setTitle:titleTemp];
  171.         [tile[i] setIconPosition:tempIconPosition];
  172.         }
  173.         
  174.   /* Create a matrix of invisible buttons that detect button presses */
  175.   /* Tag numbers of the form (row * MAXSIZEX + col) tell the slideTile: */
  176.   /* method where the button was pressed */ 
  177.     for (row=0; row<MAXSIZEY; row++) 
  178.         for (col=0; col<MAXSIZEX; col++) {
  179.             tileInPosition[row][col].invisibleID = [Button new];
  180.             [tileInPosition[row][col].invisibleID setTransparent:(BOOL)YES];
  181.             [tileInPosition[row][col].invisibleID setTag:(row * MAXSIZEX + col)];
  182.             [tileInPosition[row][col].invisibleID setTarget:self];
  183.             [tileInPosition[row][col].invisibleID setAction:@selector(slideTile:)];
  184.             }
  185.  
  186.   /* add the tile to the tileBox */
  187.     for (row=0; row<NumTilesY; row++) 
  188.         for (col=0; col<NumTilesX; col++) {
  189.             [tileBox addSubview:tile[row * NumTilesX + col]];
  190.             }
  191.     [tile[NumTilesX*NumTilesY-1] removeFromSuperview];    // remove one for the space
  192.  
  193.   /* add the invisible tiles to the tileBox (added last so they are on top) */
  194.     for (row=0; row<NumTilesY; row++) 
  195.         for (col=0; col<NumTilesX; col++) {
  196.             [tileBox addSubview:tileInPosition[row][col].invisibleID];
  197.             }
  198.     [tileSlideCV addSubview:tileBox];
  199.     tmpstr = NXGetDefaultValue ([NXApp appName], "BoardLocation");
  200.     if (sscanf(tmpstr, "%d %d", &x,&y) != 2) { x = 314; y = 314; }
  201.     [slideWindowOut moveTo:(NXCoord)x :(NXCoord)y];    
  202.     [self shuffle:mixPattern];
  203.     [self randomMix:self];
  204.     [self sizeSlideBoard];
  205.     [slideWindowOut orderFront:self];
  206.  
  207.   /* register the tile slide window with the workspace (to accept pictures) */
  208.   /* (see Controller.m from Acceptor demo app) */
  209.     listener = [Listener new];
  210.     [listener setDelegate:self];
  211.     [listener usePrivatePort];
  212.     [listener addPort];
  213.     NXConvertWinNumToGlobal([slideWindowOut windowNum], &windowNum);
  214.     [speaker setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
  215.     [speaker registerWindow:windowNum toPort:[listener listenPort]];
  216.   /* enable the soundGenerator object (get the DSP) */
  217.     if ([soundGenerator enable] == NO) {
  218.         NXRunAlertPanel(NULL,"Can't open the DSP.", "That's life.", NULL, NULL);
  219.         [soundSwitch setIntValue:0];
  220.         }
  221.  
  222.     return self;
  223. }
  224.  
  225. - appDidHide:app
  226. // responds as application's delegate
  227. {
  228.     [soundGenerator disable];
  229.     if ([infoPanelOut isVisible] == YES)    // if info panel is open, animation is happening
  230.         DPSRemoveTimedEntry (infoTimer);    // STOP IT!
  231.     return self;
  232. }
  233.  
  234. - appDidUnhide:app
  235. // responds as application's delegate
  236. {
  237.     void infoAnimatorTimeout();
  238.     if ([soundGenerator enable] == NO) {
  239.         NXRunAlertPanel(NULL,"Can't open the DSP.", "That's life.", NULL, NULL);
  240.         [soundSwitch setIntValue:0];
  241.         }
  242.     if ([infoPanelOut isVisible] == YES)    // if info panel is open, restart animation
  243.         infoTimer = DPSAddTimedEntry((double) INFOTIMERPERIOD, &infoAnimatorTimeout, self, NX_BASETHRESHOLD);
  244.     return self;
  245. }
  246.  
  247. - init
  248. // Initialize a few variables.  Some of these things could be changed while
  249. // running if a user interface to them is added (perhaps a preferrences panel)
  250. {
  251.     [super init];
  252.     movementSteps = 3;    // number of animation steps when moving tiles
  253.     currentStep = 0;
  254.     slidePeriod = .01;     // seconds of delay between animation frames
  255.     topMargin = 15;        // does not include window's titlebar
  256.     bottomMargin = 53;    // between sizebar and bottom of tilebox
  257.     leftMargin = 10;
  258.     rightMargin = 15;
  259.     interTileDistance = 2;
  260.     buttonBorderWidth = 3;    // lines which make up border of tile buttons
  261.     pictureType = TYPENONE;
  262.     infoTimer = NULL;
  263.     mixPattern = RANDOMFLAG;
  264.     solved = NO;
  265.     
  266.     return self;
  267. }
  268.  
  269. - free
  270. {
  271.     int i,row,col;
  272.     [picture free];
  273.     [tileBox free];
  274.     [clearBox free];
  275.     [myFontManager free];
  276.     for (i=0; i<(MAXSIZEX*MAXSIZEY); i++) 
  277.         [tile[i] free];
  278.     for (row=0; row<MAXSIZEY; row++) 
  279.         for (col=0; col<MAXSIZEX; col++) 
  280.             [tileInPosition[row][col].invisibleID free];
  281.     return [super free];
  282. }
  283.  
  284. - openInfoPanel:sender
  285. // show info panel and start info panel animation
  286. {
  287.     void infoAnimatorTimeout();
  288.     [infoPanelOut orderFront:self];
  289.     if (!infoTimer)
  290.         infoTimer = DPSAddTimedEntry((double) INFOTIMERPERIOD, &infoAnimatorTimeout, self, NX_BASETHRESHOLD);
  291.     return self;
  292. }
  293.  
  294. - windowWillClose:sender
  295. // respond as delegate for info panel
  296. {
  297.     DPSRemoveTimedEntry (infoTimer);
  298.     infoTimer = NULL;
  299.  
  300.     return self;
  301. }
  302.  
  303. - windowDidMove:sender;
  304. // save new board position in defaults database
  305. {
  306.     NXRect theRect;
  307.     char str[20];
  308.     
  309.     [slideWindowOut getFrame:(NXRect *)&theRect];
  310.     sprintf (str, "%d %d",(int)theRect.origin.x,(int)theRect.origin.y);
  311.     NXWriteDefault ([NXApp appName], "BoardLocation", str);
  312.     return self;
  313. }
  314.  
  315. void infoAnimatorTimeout (DPSTimedEntry timedEntry, double timeNow, void *data)
  316. // dummy method to invoke changeInfoPanel method in response to info panel animation timeout
  317. {
  318.     [(id)data changeInfoPanel];
  319. }
  320.  
  321. - (void)changeInfoPanel
  322. // Move buttons on info panel to create animation!!
  323. {
  324.     NXRect contentViewFrame;
  325.     
  326.     [infoPanelCV getFrame:&contentViewFrame];
  327.     [stripe1Blanker moveTo:(NXCoord)stripe1Positionx:(NXCoord)stripe1Positiony];
  328.     if (stripe1Positiony+stripe1Movement < 5)
  329.         stripe1Movement = -stripe1Movement;
  330.     if (stripe1Positiony+stripe1Movement+33 > contentViewFrame.size.height)
  331.         stripe1Movement = -stripe1Movement;
  332.     stripe1Positiony += stripe1Movement;
  333.     [stripe1 moveTo:(NXCoord)stripe1Positionx :(NXCoord)stripe1Positiony];
  334.     [stripe1Blanker display];
  335.     [stripe1 display];
  336.  
  337.     [stripe2Blanker moveTo:(NXCoord)stripe2Positionx:(NXCoord)stripe2Positiony];
  338.     if (stripe2Positiony+stripe2Movement < 5)
  339.         stripe2Movement = -stripe2Movement;
  340.     if (stripe2Positiony+stripe2Movement+33 > contentViewFrame.size.height)
  341.         stripe2Movement = -stripe2Movement;
  342.     stripe2Positiony += stripe2Movement;
  343.     [stripe2 moveTo:(NXCoord)stripe2Positionx :(NXCoord)stripe2Positiony];
  344.     [stripe2Blanker display];
  345.     [stripe2 display];
  346.     return;
  347. }
  348.  
  349. - infoPanelNotes:sender
  350. // plays a random 2-note chord when a button on info panel is pressed
  351. {
  352.     int key;
  353.     
  354.     key = random() % NUMBEROFBARS;
  355.  
  356.  
  357.     return self;
  358. }
  359.  
  360. - openBackground:sender
  361. // select background to load via open panel
  362. {
  363.     char *theFile,*theDirectory;
  364.     const char *const *returnedFileList;
  365.     char **fileTypeList;
  366.     char *typeList[3];
  367.     NXSize theSize;
  368.     int length;
  369.     
  370.     typeList[0] = "tiff";
  371.     typeList[1] = "eps";
  372.     typeList[2] = 0;
  373.     fileTypeList = typeList;
  374.     
  375.     myOpenPanel = [OpenPanel new];
  376.     [myOpenPanel allowMultipleFiles:(BOOL)NO];
  377.     if ([myOpenPanel runModalForTypes:(const char *const *)fileTypeList] == 0) 
  378.         return self;    // CANCEL selected
  379.     returnedFileList = [myOpenPanel filenames];
  380.     if (returnedFileList == NULL) return self;
  381.     theFile = (char *) *returnedFileList;
  382.     theDirectory = (char *) [myOpenPanel directory];
  383.     length = (strlen(theFile) + strlen(theDirectory)+1);
  384.     if (filePathLength <= length) {
  385.         if (filePath) {
  386.             free(filePath);
  387.             }
  388.         filePath = (char *)malloc(length + 1);
  389.         filePathLength = length;
  390.         }
  391.  
  392.     strcpy(filePath, theDirectory);
  393.     strcat(filePath, "/");
  394.     strcat(filePath, theFile);
  395.  
  396.     if ([self initAndCheckPicture:(char *)filePath] == NO)
  397.         return self;    // selected file is not loadable
  398.     else {
  399.         if (usePictures == NO){
  400.             usePictures = YES;
  401.             [usePicturesSwitch setIntValue:1];
  402.             NXWriteDefault ([NXApp appName], "UsePictures", "YES");
  403.             }
  404.         [picture getSize:&theSize];
  405.         [picture getSize:(NXSize *)&theSize];
  406.         if ((theSize.height >= NUMBERMINHEIGHT) && (theSize.width >= NUMBERMINWIDTH)) {
  407.             [useNumbersSwitch setEnabled:YES];
  408.             if (useNumbers == YES)
  409.                 [usePicturesSwitch setEnabled:YES];
  410.             }
  411.         else {            // picture too small to allow using numbers
  412.             useNumbers = NO;
  413.             [useNumbersSwitch setEnabled:NO];
  414.             [useNumbersSwitch setIntValue:0];
  415.             [usePicturesSwitch setEnabled:NO];
  416.             }
  417.             [self sizeSlideBoard];
  418.         }
  419.     return self;
  420. }
  421.  
  422. - togglePictures:sender
  423. // Toggles pictures on and off. Receives action messages from the "Picture" switch.
  424. {
  425.     if ([sender intValue] == 1) {
  426.         usePictures = YES;
  427.         NXWriteDefault ([NXApp appName], "UsePictures", "YES");
  428.         [useNumbersSwitch setEnabled:YES];
  429.         }
  430.     else {
  431.         usePictures = NO;
  432.         NXWriteDefault ([NXApp appName], "UsePictures", "NO");
  433.         [useNumbersSwitch setEnabled:NO];
  434.         };
  435.     [self sizeSlideBoard];
  436.         
  437.     return self;
  438. }
  439.  
  440. - toggleNumbers:sender
  441. // Toggles visibility of numbers on and off.
  442. // Receives action messages from the "Numbers" switch.
  443. {
  444.     if ([sender intValue] == 1) {
  445.         useNumbers = YES;
  446.         NXWriteDefault ([NXApp appName], "UseNumbers", "YES");
  447.         if (pictureLoaded == YES)
  448.             [usePicturesSwitch setEnabled:YES];
  449.         }
  450.     else {
  451.         useNumbers = NO;
  452.         NXWriteDefault ([NXApp appName], "UseNumbers", "NO");
  453.         [usePicturesSwitch setEnabled:NO];
  454.         };
  455.     [self sizeSlideBoard];
  456.     
  457.     return self;
  458. }
  459.  
  460. - sizeSlideBoard
  461. // Draws the tile board at the correct size, with the NumTilesX * NumTilesY tiles
  462. // Determines the size from the current size of "picture" if the "Picture" switch
  463. // is checked, and from the size of the window if not.
  464. {
  465.     NXRect tempRect;
  466.     NXSize tempSize;
  467.     NXPoint tempPoint;
  468.     id tempImage,newFont;
  469.     int    i,row,col,minSize;
  470.     float tempFontSize;
  471.     
  472.     if (usePictures == YES) {
  473.         [slideWindowOut disableFlushWindow];
  474.         [picture getSize:&tempSize];
  475.         sizex = (int)((tempSize.width/NumTilesX)-buttonBorderWidth);
  476.         sizey = (int)((tempSize.height/NumTilesY)-buttonBorderWidth);
  477.         tempSize.width = (NXCoord)sizex;
  478.         tempSize.height = (NXCoord)sizey;
  479.         tempRect.size = tempSize;
  480.         if (sizex>sizey) minSize = sizey;
  481.         else minSize = sizex;
  482.         tempFontSize = (float)((minSize-5+buttonBorderWidth)/1.6);
  483.         newFont = [myFontManager findFont:(const char *)"Helvetica" traits:
  484.                     (NXFontTraitMask)NX_UNBOLD weight:(int)0 size:tempFontSize];
  485.         for (row=0; row<NumTilesY; row++) 
  486.             for (col=0; col<NumTilesX; col++){
  487.                 tempPoint.x = (NXCoord)((sizex+buttonBorderWidth) * col);
  488.                 tileInPosition[row][col].xPosition = tempPoint.x;
  489.                 tempPoint.y = (NXCoord)((sizey+buttonBorderWidth) * (NumTilesY-row-1));
  490.                 tileInPosition[row][col].yPosition = tempPoint.y;
  491.                 tempRect.origin = tempPoint;
  492.                 tempImage = [[NXImage alloc] initFromImage:(NXImage *)picture rect:(const NXRect *)&tempRect];
  493.                 [tile[row*NumTilesX+col] setImage:tempImage];
  494.  
  495.                 [tile[row*NumTilesX+col] sizeTo:(NXCoord)(sizex+buttonBorderWidth)
  496.                     :(NXCoord)(sizey+buttonBorderWidth)];
  497.                 [tile[tileInPosition[row][col].tileNumber] moveTo:
  498.                     (tileInPosition[row][col].xPosition) :(tileInPosition[row][col].yPosition)];
  499.                 [tileInPosition[row][col].invisibleID moveTo:
  500.                     (tileInPosition[row][col].xPosition) :(tileInPosition[row][col].yPosition)];
  501.                 [tileInPosition[row][col].invisibleID sizeTo:
  502.                     (NXCoord)(sizex+buttonBorderWidth) :(NXCoord)(sizey+buttonBorderWidth)];
  503.                 if (useNumbers == YES)
  504.                     [tile[row*NumTilesX+col] setIconPosition:NX_ICONOVERLAPS];
  505.                 else
  506.                     [tile[row*NumTilesX+col] setIconPosition:NX_ICONONLY];
  507.                 }
  508.         [clearBox sizeTo:(NXCoord)(sizex+buttonBorderWidth) :(NXCoord)(sizey+buttonBorderWidth)];
  509.         [tileBox setBorderType:NX_BEZEL]; 
  510.         [tileBox sizeToFit];
  511.         [tileBox getFrame:&tempRect];
  512.         windowSize.width = (NXCoord)(tempRect.size.width + leftMargin + rightMargin);
  513.         windowSize.height = (NXCoord)(tempRect.size.height + topMargin + bottomMargin);
  514.         [slideWindowOut  sizeWindow:windowSize.width :windowSize.height];
  515.         [tileBox moveTo:(NXCoord)leftMargin :(NXCoord)bottomMargin];
  516.  
  517.         // I don't think the display in the following loop should be necessary, but set font
  518.         // doesn't seem to work unless you do a display after setting icon position
  519.         if (useNumbers == YES) {
  520.             for (i=0; i<(MAXSIZEX*MAXSIZEY); i++) {
  521.                 [tile[i] display]; 
  522.                 [tile[i] setFont:newFont];
  523.                 }
  524.             }
  525.             
  526.         [slideWindowOut reenableFlushWindow];
  527.         [slideWindowOut display];
  528.         }
  529.  
  530.     else {
  531.         [slideWindowOut disableFlushWindow];
  532.         [tileSlideCV getFrame:&tempRect];
  533.         sizex = (int) ((tempRect.size.width - leftMargin - rightMargin -((NumTilesX-1) * interTileDistance))/NumTilesX);
  534.         sizey = (int) ((tempRect.size.height - topMargin - bottomMargin -((NumTilesY-1) * interTileDistance))/NumTilesY);
  535.     
  536.         for (row=0; row<NumTilesY; row++) 
  537.             for (col=0; col<NumTilesX; col++){
  538.                 tileInPosition[row][col].xPosition =(NXCoord)((sizex+interTileDistance) * col);
  539.                 tileInPosition[row][col].yPosition =(NXCoord)((sizey+interTileDistance) * (NumTilesY-row-1));
  540.                 [tile[tileInPosition[row][col].tileNumber] moveTo:
  541.                     (tileInPosition[row][col].xPosition) :(tileInPosition[row][col].yPosition)];
  542.                 [tileInPosition[row][col].invisibleID moveTo:
  543.                     (tileInPosition[row][col].xPosition) :(tileInPosition[row][col].yPosition)];
  544.                 [tile[tileInPosition[row][col].tileNumber] sizeTo:(NXCoord)sizex :(NXCoord)sizey];
  545.                 [tileInPosition[row][col].invisibleID sizeTo:(NXCoord)sizex :(NXCoord)sizey];
  546.                 }
  547.         if (sizex>sizey) minSize = sizey;
  548.         else minSize = sizex;
  549.         for (i=0; i<(MAXSIZEX*MAXSIZEY); i++) {
  550.             [tile[i] setIconPosition:NX_TITLEONLY];
  551.             [tile[i] display];
  552.             [tile[i] setFont:[myFontManager findFont:(const char *)"Helvetica" traits:(NXFontTraitMask)NX_UNBOLD weight:(int)0 size:(float)((minSize-5)/1.6)]];
  553.             }
  554.     
  555.         [clearBox sizeTo:(NXCoord)sizex :(NXCoord)sizey];
  556.         [tileBox setBorderType:NX_BEZEL]; 
  557.         [tileBox sizeToFit];
  558.         [tileBox moveTo:(NXCoord)leftMargin :(NXCoord)bottomMargin];
  559.         [slideWindowOut reenableFlushWindow];
  560.         [slideWindowOut display];
  561.         }
  562.     
  563.     return self;    
  564. }
  565.  
  566. - slideTile:sender
  567. // Receives action messages from the invisible buttons over the tiles
  568. // Starts sliding animation timer if sliding is to be done. 
  569. {
  570.     void slideTimeout ();
  571.     int row,col;
  572.     
  573.     if (solved == YES) return self;
  574.     if (sliding == YES) return self;
  575.     row = (int) [sender tag]/MAXSIZEX;
  576.     col = (int) [sender tag]-(row*MAXSIZEX);
  577.     if ((rowOfSpace != row) && (colOfSpace != col)) return self;
  578.     if ((rowOfSpace == row) && (colOfSpace == col)) return self;
  579.  
  580.  
  581.     if (rowOfSpace == row) {
  582.         slideRowOrCol = ROW;
  583.         howManyToMove = colOfSpace - col;
  584.         }
  585.     if (colOfSpace == col) { 
  586.         slideRowOrCol = COL;
  587.         howManyToMove = rowOfSpace - row;
  588.         }
  589.     numberOfMoves++;
  590.     [movesOut setIntValue:numberOfMoves];
  591.  
  592.     sliding = (BOOL)YES;
  593.     fromRow = row;
  594.     fromCol = col;
  595.     slideTimer = DPSAddTimedEntry((double) slidePeriod, &slideTimeout, self, NX_BASETHRESHOLD);
  596.  
  597.     return self;
  598. }
  599.  
  600. void slideTimeout (DPSTimedEntry timedEntry, double timeNow, void *data)
  601. // Dummy method to invoke slideABit method in response to tile sliding timeout
  602. {
  603.     [(id)data slideABit];
  604. }
  605.  
  606. - (void) slideABit
  607. // Slide tiles one step; re-arrange position of tiles when done sliding.
  608. // Check for puzzle solved, congratulate and re-shuffle if it is
  609. {
  610.     int    distance,i,direction;
  611.     int row,col,difficulty;
  612.     char str[100];
  613.     
  614.     currentStep++;
  615.     if (howManyToMove < 0) 
  616.         direction = (-1);
  617.     else 
  618.         direction = 1;
  619.  
  620.     if (currentStep == movementSteps) {        // done sliding; re-arrange position of tiles
  621.         DPSRemoveTimedEntry (slideTimer);
  622.         sliding = (BOOL)NO;
  623.         if (slideRowOrCol == ROW) {
  624.             for (i=(fromCol + howManyToMove);i!= fromCol;i=i-direction) {
  625.                 [tile[tileInPosition[fromRow][i-direction].tileNumber] addSubview:clearBox];
  626.                 [clearBox display];
  627.                 [clearBox removeFromSuperview];
  628.                 tileInPosition[fromRow][i].tileNumber = tileInPosition[fromRow][i-direction].tileNumber;
  629.                 [tile[tileInPosition[fromRow][i].tileNumber] moveTo:
  630.                     (tileInPosition[fromRow][i].xPosition) :(tileInPosition[fromRow][i].yPosition)];
  631.                 [tile[tileInPosition[fromRow][i].tileNumber] display];
  632.                 }
  633.             }
  634.         else {
  635.             for (i=(fromRow + howManyToMove);i!= fromRow;i=i-direction) {
  636.                 [tile[tileInPosition[i-direction][fromCol].tileNumber] addSubview:clearBox];
  637.                 [clearBox display];
  638.                 [clearBox removeFromSuperview];
  639.                 tileInPosition[i][fromCol].tileNumber = tileInPosition[i-direction][fromCol].tileNumber;
  640.                 [tile[tileInPosition[i][fromCol].tileNumber] moveTo:
  641.                     (tileInPosition[i][fromCol].xPosition) :(tileInPosition[i][fromCol].yPosition)];
  642.                 [tile[tileInPosition[i][fromCol].tileNumber] display]; 
  643.                 }
  644.             }
  645.         tileInPosition[fromRow][fromCol].tileNumber = NumTilesX*NumTilesY-1;
  646.         // check for puzzle solved
  647.         solved = YES;
  648.         for (row=0; row<NumTilesY; row++) 
  649.             for (col=0; col<NumTilesX; col++)
  650.                 if (tileInPosition[row][col].tileNumber != (row * NumTilesX + col)) solved = NO;
  651.         rowOfSpace = fromRow;
  652.         colOfSpace = fromCol;
  653.         if (solved == YES) {
  654.             [tileInPosition[NumTilesY-1][NumTilesX-1].invisibleID removeFromSuperview];
  655.             [tileBox addSubview:tile[NumTilesX*NumTilesY-1]];
  656.             [tileBox addSubview:tileInPosition[NumTilesY-1][NumTilesX-1].invisibleID];
  657.             [tile[NumTilesX*NumTilesY-1] moveTo:(tileInPosition[NumTilesY-1][NumTilesX-1].xPosition)
  658.                 :(tileInPosition [NumTilesY-1][NumTilesX-1].yPosition)];
  659.             [tile[NumTilesX*NumTilesY-1] display];
  660.             [self playFinishMusic];
  661.             difficulty = NumTilesX-3;
  662.             if ((mixPattern != RANDOMFLAG) && (numberOfMoves < bests[difficulty][mixPattern])) {
  663.                 bests[difficulty][mixPattern] = numberOfMoves;
  664.                 [[bestsMatrixOut cellAt:difficulty:mixPattern]
  665.                     setIntValue:bests[difficulty][mixPattern]];
  666.                 [[bestsMatrixOut cellAt:difficulty:mixPattern] setBackgroundGray:(float)NX_WHITE];
  667.                 [bestsPanelOut orderFront:self];
  668.                 NXRunAlertPanel("Congratulations!",
  669.                     "You have set a new 'best'!", "Yippee!", NULL, NULL);
  670.                 switch (difficulty) {
  671.                     case 0:
  672.                         sprintf (str, "%d %d %d %d\0", 
  673.                             bests[0][0], bests[0][1], bests[0][2], bests[0][3]);
  674.                         NXWriteDefault ([NXApp appName], "EasyBests", str);
  675.                         break;
  676.                     case 1:
  677.                         sprintf (str, "%d %d %d %d\0", 
  678.                             bests[1][0], bests[1][1], bests[1][2], bests[1][3]);
  679.                         NXWriteDefault ([NXApp appName], "NormalBests", str);
  680.                         break;
  681.                     case 2:
  682.                         sprintf (str, "%d %d %d %d\0", 
  683.                             bests[2][0], bests[2][1], bests[2][2], bests[2][3]);
  684.                         NXWriteDefault ([NXApp appName], "HardBests", str);
  685.                         break;
  686.                     }
  687.                 [[bestsMatrixOut cellAt:difficulty:mixPattern] setBackgroundGray:(float)NX_LTGRAY];
  688.                 }
  689.             }
  690.         currentStep = 0;
  691.         }
  692.     else
  693.         if (slideRowOrCol == ROW) {        // not done sliding; move tiles one step
  694.             distance = (int) (((float)currentStep/(float)movementSteps) * sizex * direction);
  695.             for (i=(fromCol + howManyToMove-direction);i!= fromCol-direction;i=i-direction) {
  696.                 [tile[tileInPosition[fromRow][i].tileNumber] addSubview:clearBox];
  697.                 [clearBox display];
  698.                 [clearBox removeFromSuperview];
  699.                 [tile[tileInPosition[fromRow][i].tileNumber] moveTo:
  700.                     (NXCoord)(tileInPosition[fromRow][i].xPosition + distance) :(tileInPosition[fromRow][i].yPosition)];
  701.                 [tile[tileInPosition[fromRow][i].tileNumber] display]; 
  702.                 }
  703.             }
  704.         else {
  705.             distance = (int) (((float)currentStep/(float)movementSteps)*sizey*direction * -1);
  706.             for (i=(fromRow + howManyToMove-direction);i!= fromRow-direction;i=i-direction) {
  707.                 [tile[tileInPosition[i][fromCol].tileNumber] addSubview:clearBox];
  708.                 [clearBox display];
  709.                 [clearBox removeFromSuperview];
  710.                 [tile[tileInPosition[i][fromCol].tileNumber] moveTo:
  711.                     (tileInPosition[i][fromCol].xPosition) :(NXCoord)(tileInPosition[i][fromCol].yPosition + distance)];
  712.                 [tile[tileInPosition[i][fromCol].tileNumber] display]; 
  713.                 }
  714.             }
  715.     return;
  716.  
  717. - playFinishMusic
  718. // plays the run of notes when the puzzle is solved
  719. {
  720.     
  721.  
  722.  
  723.     return self;
  724. }
  725.  
  726. - selectSize:sender
  727. // Receives action messages from the "Difficulty" menu cells
  728. // Selects x and y size of tile board (currently the same) and invokes "shuffle:" 
  729. {
  730.     int    row,col;
  731.     char str[3];
  732.  
  733.     for (row=0; row<NumTilesY; row++) 
  734.         for (col=0; col<NumTilesX; col++){
  735.             [tile[tileInPosition[row][col].tileNumber] removeFromSuperview];
  736.             [tileInPosition[row][col].invisibleID removeFromSuperview];
  737.             }
  738.     [tileBox setBorderType:NX_NOBORDER];        // clear old board 
  739.     [tileSlideCV display];
  740.  
  741.     // set number of tiles 
  742.     NumTilesX = [[sender selectedCell] tag];
  743.     NumTilesY = NumTilesX;
  744.  
  745.     sprintf (str, "%d\0", NumTilesX);
  746.     NXWriteDefault ([NXApp appName], "Difficulty", str);
  747.  
  748.     for (row=0; row<NumTilesY; row++) 
  749.         for (col=0; col<NumTilesX; col++) {
  750.             tileInPosition[row][col].tileNumber = (row * NumTilesX)+ col;
  751.             [tileBox addSubview:tile[tileInPosition[row][col].tileNumber]];
  752.             }
  753.     [tile[NumTilesX*NumTilesY-1] removeFromSuperview];
  754.     for (row=0; row<NumTilesY; row++)         // invisible buttons added last so they are on top!
  755.         for (col=0; col<NumTilesX; col++) {
  756.             [tileBox addSubview:tileInPosition[row][col].invisibleID];
  757.             }
  758.     [tile[NumTilesX*NumTilesY-1] removeFromSuperview];
  759.  
  760.     if (mixPattern != RANDOMFLAG) {
  761.         [self shuffle:mixPattern];
  762.         [self sizeSlideBoard];
  763.         [slideWindowOut orderFront:self];
  764.         }
  765.     else {
  766.         [self shuffle:mixPattern];
  767.         [self randomMix:self];
  768.         }
  769.     return self;
  770. }
  771.  
  772. - shuffle:(int)pattern
  773. // shuffle the board (to a default shuffled pattern) and reset numberOfMoves
  774. // Currently, this only handles 3*3, 4*4, and 5*5 boards
  775. {
  776.     static int scrambledSize5[NUMOFCHALLENGES][25]=    
  777.         {{24,10,8,22,5,7,15,16,20,23,17,12,2,14,6,3,13,4,18,1,0,19,11,9,21},
  778.         {24,10,14,13,22,9,21,1,3,11,4,8,16,23,18,2,6,20,15,17,0,7,19,5,12},
  779.         {24,19,14,9,4,23,18,13,8,3,22,17,12,7,2,21,16,11,6,1,20,15,10,5,0},
  780.         {24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0}};
  781.     static int scrambledSize4[NUMOFCHALLENGES][16]=
  782.         {{15,14,3,13,0,9,11,4,10,7,5,2,8,1,6,12},
  783.         {15,3,5,13,1,11,12,2,4,10,0,8,6,14,9,7},
  784.         {15,11,7,3,14,10,6,2,13,9,5,1,12,8,4,0},
  785.         {15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0}};
  786.     static int scrambledSize3[NUMOFCHALLENGES][9]=
  787.         {{8,0,4,2,5,3,6,1,7},
  788.         {8,1,5,6,4,3,0,2,7},
  789.         {8,5,2,6,4,1,7,3,0},
  790.         {8,1,5,7,4,6,2,3,0}};
  791.     int row,col,patternUsed;
  792.  
  793.     patternUsed = pattern % NUMOFCHALLENGES;
  794.     switch (NumTilesX) {
  795.         case 3:
  796.             for (row=0;row<NumTilesY;row++)
  797.                 for (col=0;col<NumTilesX;col++)
  798.                     tileInPosition[row][col].tileNumber = 
  799.                         (int)scrambledSize3[patternUsed][col + (row *NumTilesX)];
  800.             break;
  801.         case 4:
  802.             for (row=0;row<NumTilesY;row++)
  803.                 for (col=0;col<NumTilesX;col++)
  804.                     tileInPosition[row][col].tileNumber = 
  805.                         (int)scrambledSize4[patternUsed][col + (row *NumTilesX)];
  806.             break;
  807.         case 5:
  808.             for (row=0;row<NumTilesY;row++)
  809.                 for (col=0;col<NumTilesX;col++)
  810.                     tileInPosition[row][col].tileNumber = 
  811.                         (int)scrambledSize5[patternUsed][col + (row *NumTilesX)];
  812.             break;
  813.         }
  814.     rowOfSpace = 0;
  815.     colOfSpace = 0;
  816.  
  817.     numberOfMoves = 0;
  818.     [movesOut setIntValue:numberOfMoves];
  819.     solved = NO;
  820.  
  821.     return self;
  822. }
  823.  
  824. - challengeMix:sender
  825. // select next pattern, call shuffle, change window title, and redraw
  826. // target of the 'Challenge Mix' menu item
  827. {
  828.     char        buffer[30],bufferNum[5];
  829.  
  830.     [tile[NumTilesX*NumTilesY-1] removeFromSuperview];
  831.     solved = NO;
  832.     if (mixPattern == RANDOMFLAG)
  833.         mixPattern = NUMOFCHALLENGES;    // wraps around when used
  834.     mixPattern ++;
  835.     if (mixPattern >= NUMOFCHALLENGES) mixPattern = 0;
  836.  
  837.     [self shuffle:mixPattern];
  838.     strcpy(buffer,"Tile Slide - Challenge#");
  839.     sprintf(bufferNum," %d", (mixPattern+1));
  840.     strcat(buffer,bufferNum);
  841.     [slideWindowOut setTitle:(const char *)buffer];
  842.     [self sizeSlideBoard];
  843.     [slideWindowOut orderFront:self];
  844.  
  845.     return self;
  846. }
  847.  
  848. - randomMix:sender
  849. // make 100 random slides, change window title, and redraw
  850. // target of the 'Random Mix' menu item
  851. {
  852.     int    i,dir,tempRow,tempCol;
  853.     int tempTileNum;
  854.     static int moves[4][2] = {{0,1},{1,0},{0,-1},{-1,0}};
  855.     char        buffer[30];
  856.     
  857.     [tile[NumTilesX*NumTilesY-1] removeFromSuperview];
  858.     solved = NO;
  859.     mixPattern = RANDOMFLAG;
  860.     for (i=0;i<100;i++) {
  861.         dir = random() % 4;
  862.         while ((rowOfSpace+moves[dir][1] >= NumTilesY) || (rowOfSpace+moves[dir][1] < 0) ||
  863.             (colOfSpace+moves[dir][0] >= NumTilesX)    || (colOfSpace+moves[dir][0] < 0))
  864.             dir = random() % 4;
  865.         tempRow = rowOfSpace+ moves[dir][1];
  866.         tempCol = colOfSpace+moves[dir][0];
  867.         tempTileNum = tileInPosition[tempRow][tempCol].tileNumber;
  868.         tileInPosition[tempRow][tempCol].tileNumber = tileInPosition[rowOfSpace][colOfSpace].tileNumber;
  869.         tileInPosition[rowOfSpace][colOfSpace].tileNumber = tempTileNum;
  870.         rowOfSpace = tempRow;
  871.         colOfSpace = tempCol;
  872.         }
  873.     strcpy(buffer,"Tile Slide - Random");
  874.     [slideWindowOut setTitle:(const char *)buffer];
  875.     numberOfMoves = 0;
  876.     [movesOut setIntValue:numberOfMoves];
  877.     [self sizeSlideBoard];
  878.     [slideWindowOut orderFront:self];
  879.     return self;
  880. }
  881.  
  882. - (int)iconEntered:(int)windowNum at:(double)x :(double)y
  883.     iconWindow:(int)iconWindowNum iconX:(double)iconX iconY:(double)iconY
  884.     iconWidth:(double)iconWidth iconHeight:(double)iconHeight
  885.     pathList:(char *)pathList
  886. // respond as delegate for main window
  887. {
  888.     char    *stringPosition;
  889.     int        length;
  890.  
  891.   /* save the file's path for later */
  892.     length = strlen(pathList);
  893.     if (filePathLength <= length) {
  894.         if (filePath) {
  895.             free(filePath);
  896.             }
  897.         filePath = (char *)malloc(length + 1);
  898.         filePathLength = length;
  899.         }
  900.     strcpy(filePath, pathList);
  901.     stringPosition = filePath;
  902.     
  903.   /* the number of tabs + 1 equals the number of files dragged in */
  904.     files = 1;
  905.     while (stringPosition = index(stringPosition, '\t')) {
  906.         files++;
  907.         stringPosition++;
  908.         }
  909.     
  910.     return 0;    
  911. }
  912.  
  913. - (int)iconReleasedAt:(double)x :(double)y ok:(int *)flag
  914. // respond as delegate for main window
  915. {
  916.     NXSize theSize;
  917.     
  918.     if (files!=1) {
  919.         *flag = 0;    // tell Workspace to animate the ICON back to its source window
  920.         return 0;
  921.         }
  922.     
  923.     if ([self initAndCheckPicture:(char *)filePath] == NO) {
  924.         *flag = 0;    // do not accept the icon
  925.         }
  926.     else {
  927.         *flag = 1;    // accept the icon
  928.         if (usePictures == NO){
  929.             usePictures = YES;
  930.             [usePicturesSwitch setIntValue:1];
  931.             NXWriteDefault ([NXApp appName], "UsePictures", "YES");
  932.             }
  933.         [picture getSize:&theSize];
  934.         [picture getSize:(NXSize *)&theSize];
  935.         if ((theSize.height >= NUMBERMINHEIGHT) && (theSize.width >= NUMBERMINWIDTH)) {
  936.             [useNumbersSwitch setEnabled:YES];
  937.             if (useNumbers == YES)
  938.                 [usePicturesSwitch setEnabled:YES];
  939.             }
  940.         else 
  941.         {                            // picture too small to allow using numbers
  942.             useNumbers = NO;
  943.             [useNumbersSwitch setEnabled:NO];
  944.             [useNumbersSwitch setIntValue:0];
  945.             [usePicturesSwitch setEnabled:NO];
  946.             }
  947.             [self sizeSlideBoard];
  948.         }
  949.     return 0;        
  950. }
  951.  
  952. - (BOOL)initAndCheckPicture:(char *)pictureName
  953. // Checks out the dragged in icon.  Makes sure it is tiff or eps 
  954. // and resizes if too big or small.  Returns YES if it loads a picture.
  955. {
  956.     NXStream *input;
  957.     id newPicture;
  958.     NXSize theSize,tempSize;
  959.     NXRect tempRect;
  960.     int tempPictureType;
  961.     char *rightmostPeriod;
  962.  
  963.     if (pictureName == NULL) return NO;
  964.     rightmostPeriod = strrchr(pictureName, '.');
  965.     if (rightmostPeriod == NULL) return NO;
  966.     tempPictureType = TYPENONE;
  967.     if (NXOrderStrings((unsigned char *) ".tiff",(unsigned char *) rightmostPeriod, YES, 5, NULL) == 0) 
  968.         tempPictureType = TYPETIFF;
  969.     if (NXOrderStrings((unsigned char *) ".eps",(unsigned char *) rightmostPeriod, YES, 4, NULL) == 0)
  970.         tempPictureType = TYPEEPS;
  971.     if (tempPictureType == TYPENONE) return NO;        // unsuccessful load
  972.  
  973.     input = NXMapFile(pictureName, NX_READONLY);
  974.     if (input == NULL) return NO;        // unsuccessful load
  975.  
  976.     newPicture = [[NXImage alloc] initFromStream:input];
  977.     [newPicture getSize:(NXSize *)&theSize];
  978.  
  979.   /* if a tiff picture is too small, it is rejected */
  980.   /* (We could just resize it, as we do for eps pictures, */
  981.   /*   but I don't like the look of resized tiffs anyways.) */ 
  982.     if (((theSize.height < IMAGEMINHEIGHT) || (theSize.width < IMAGEMINWIDTH))
  983.             && (tempPictureType == TYPETIFF))
  984.         {
  985.         [newPicture free];
  986.         NXCloseMemory(input,NX_FREEBUFFER);
  987.         return NO;        // unsuccessful load
  988.         }
  989.  
  990.   /* we now know we have a valid picture, so get rid of the old one */
  991.     [picture free];
  992.     picture = newPicture;
  993.  
  994.   /* if a tiff picture is too large, use only the bottom left section */
  995.     if (((theSize.height > IMAGEMAXHEIGHT) || (theSize.width > IMAGEMAXWIDTH))
  996.             && (tempPictureType == TYPETIFF))
  997.         {
  998.         if (theSize.width > IMAGEMAXWIDTH)
  999.             tempRect.size.width = IMAGEMAXWIDTH;
  1000.         else
  1001.             tempRect.size.width = theSize.width;
  1002.         if (theSize.height > IMAGEMAXHEIGHT)
  1003.             tempRect.size.height = IMAGEMAXHEIGHT;
  1004.         else
  1005.             tempRect.size.height = theSize.height;
  1006.         tempRect.origin.x = 0;
  1007.         tempRect.origin.y = 0;
  1008.         picture = [[NXImage alloc] initFromImage:(NXImage *)newPicture rect:(const NXRect *)&tempRect];
  1009.         }
  1010.     
  1011.   /* if an eps picture is too small, we resize it */
  1012.     if (((theSize.height < IMAGEMINHEIGHT) || (theSize.width < IMAGEMINWIDTH))
  1013.             && (tempPictureType == TYPEEPS))
  1014.         {
  1015.         if (theSize.width < IMAGEMINWIDTH)
  1016.             tempSize.width = IMAGEMINWIDTH;
  1017.         else
  1018.             tempSize.width = theSize.width;
  1019.         if (theSize.height < IMAGEMINHEIGHT)
  1020.             tempSize.height = IMAGEMINHEIGHT;
  1021.         else
  1022.             tempSize.height = theSize.height;
  1023.         [newPicture setSize:&tempSize];
  1024.         }
  1025.  
  1026.   /* if an eps picture is too large, we resize it */
  1027.     if (((theSize.height > IMAGEMAXHEIGHT) || (theSize.width > IMAGEMAXWIDTH))
  1028.             && (tempPictureType == TYPEEPS))
  1029.         {
  1030.         if (theSize.width > IMAGEMAXWIDTH)
  1031.             tempSize.width = IMAGEMAXWIDTH;
  1032.         else
  1033.             tempSize.width = theSize.width;
  1034.         if (theSize.height > IMAGEMAXHEIGHT)
  1035.             tempSize.height = IMAGEMAXHEIGHT;
  1036.         else
  1037.             tempSize.height = theSize.height;
  1038.         [newPicture setSize:&tempSize];
  1039.         }
  1040.  
  1041.   /* set eps pictures to be scalable */
  1042.     if (tempPictureType == TYPEEPS) {
  1043.         [picture setScalable:(BOOL)YES]; 
  1044.         [picture setDataRetained:(BOOL)YES]; 
  1045.         } 
  1046.  
  1047.     pictureType = tempPictureType;
  1048.     pictureLoaded = YES;
  1049.     NXCloseMemory(input,NX_FREEBUFFER);
  1050.     NXWriteDefault ([NXApp appName], "Picture", filePath);
  1051.     return YES;
  1052. }
  1053.  
  1054. - windowWillResize:sender toSize:(NXSize *)frameSize
  1055. // Acts as delegate for main window confining window resizing
  1056. {
  1057.     if ( useNumbers == YES) 
  1058.         {
  1059.         if (frameSize->width < (NXCoord)(NUMBERMINWIDTH+leftMargin+rightMargin+2))
  1060.             frameSize->width = (NXCoord)(NUMBERMINWIDTH+leftMargin+rightMargin+2);
  1061.         if (frameSize->height < (NXCoord)(NUMBERMINHEIGHT+23+9+topMargin+bottomMargin))
  1062.             frameSize->height = (NXCoord)(NUMBERMINHEIGHT+23+9+topMargin+bottomMargin);
  1063.         }
  1064.     if ( usePictures == YES) 
  1065.         if (pictureType == TYPEEPS) {
  1066.             if (frameSize->width < (NXCoord)(IMAGEMINWIDTH+leftMargin+rightMargin+2))
  1067.                 frameSize->width = (NXCoord)(IMAGEMINWIDTH+leftMargin+rightMargin+2);
  1068.             if (frameSize->height < (NXCoord)(IMAGEMINHEIGHT+23+9+topMargin+bottomMargin))
  1069.                 frameSize->height = (NXCoord)(IMAGEMINHEIGHT+23+9+topMargin+bottomMargin);
  1070.             if (frameSize->width > (NXCoord)(IMAGEMAXWIDTH+leftMargin+rightMargin+2))
  1071.                 frameSize->width = (NXCoord)(IMAGEMAXWIDTH+leftMargin+rightMargin+2);
  1072.             if (frameSize->height > (NXCoord)(IMAGEMAXHEIGHT+23+9+topMargin+bottomMargin))
  1073.                 frameSize->height = (NXCoord)(IMAGEMAXHEIGHT+23+9+topMargin+bottomMargin);
  1074.             }
  1075.         else {        // I don't like the look of resized tiffs, so don't allow resizing
  1076.             frameSize->width = (windowSize.width+2);
  1077.             frameSize->height = (windowSize.height+23+9);
  1078.             }
  1079.     return self;
  1080. }
  1081.  
  1082. - windowDidResize:sender;
  1083. // Acts as delegate for main window. Enable or disable usePicturesSwitch and
  1084. // useNumbersSwitch according to new size.  Resize picture and redraw.
  1085. {
  1086.     NXRect theRect;
  1087.     NXSize theSize;
  1088.     
  1089.     [tileSlideCV getFrame:(NXRect *)&theRect];
  1090.     theSize.width = theRect.size.width - leftMargin - rightMargin;
  1091.     theSize.height = theRect.size.height - topMargin - bottomMargin;
  1092.  
  1093.     if ((theSize.height >= IMAGEMINHEIGHT) && (theSize.width >= IMAGEMINWIDTH) 
  1094.             && (theSize.height <= IMAGEMAXHEIGHT) && (theSize.width <= IMAGEMAXWIDTH) 
  1095.             && (pictureLoaded == YES) && (useNumbers == TRUE))
  1096.         [usePicturesSwitch setEnabled:YES];
  1097.     else 
  1098.         [usePicturesSwitch setEnabled:NO];
  1099.  
  1100.     if ((theSize.height >= NUMBERMINHEIGHT) && (theSize.width >= NUMBERMINWIDTH)
  1101.             && (usePictures == TRUE))
  1102.         [useNumbersSwitch setEnabled:YES];
  1103.     else 
  1104.         [useNumbersSwitch setEnabled:NO];
  1105.  
  1106.     if ( usePictures == NO) {
  1107.         if (pictureType == TYPEEPS)
  1108.             [picture setSize:&theSize];
  1109.         [self sizeSlideBoard];
  1110.         }
  1111.     else 
  1112.         if (pictureType == TYPEEPS) {
  1113.             [picture setSize:&theSize];
  1114.             [self sizeSlideBoard];
  1115.             }
  1116.     
  1117.     return self;
  1118. }
  1119.  
  1120. @end
  1121.